package com.apes.framework.plugin.esb.processor.connector.http;

import com.alibaba.fastjson.JSONObject;
import com.apes.framework.api.SpringManager;
import com.apes.framework.plugin.esb.api.Request;
import com.apes.framework.plugin.esb.processor.connector.Connector;
import com.apes.framework.util.MapUtil;
import com.apes.framework.util.Tools;
import lombok.Data;
import org.apache.http.*;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.FormBodyPart;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.util.Assert;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;

/**
 * 功能：Http连接器类：HttpClient4实现
 *
 * @author xul
 * @create 2020-04-23 10:13
 */
@Data
public class HttpClient4Connector<T extends Request, R> implements Connector<T, R> {
    private final String CHARSET_UTF8_NAME = "UTF-8";
    private final Charset CHARSET_UTF8 = Charset.forName(CHARSET_UTF8_NAME);
    private final ContentType TEXT_PLAIN_UTF8 = ContentType.create("text/plain", CHARSET_UTF8);
    private final ContentType APPLICATION_FORM_URLENCODED_UTF8 = ContentType.create("application/x-www-form-urlencoded", CHARSET_UTF8);
    private final ContentType MULTIPART_FORM_DATA_UTF8 = ContentType.create("multipart/form-data", CHARSET_UTF8);

    private String url = null;
    private Map<String, String> headers = null;
    private String action = "POST";
    private String responseType;
    private Boolean returnHeaders = false;

    @Override
    public void connect() {
        url = Tools.replace(url);
    }

    @Override
    public void disconnect() {
    }

    @Override
    public R process(T data) {
        Object payload = data.getData();
        Assert.notNull(payload, this.getClass().getName() + ": body must not be null.");
        CloseableHttpClient client = SpringManager.getBean(CloseableHttpClient.class);
        HttpRequestBase httpMethod = createHttpMethod(payload);
        try (CloseableHttpResponse response = client.execute(httpMethod)) {
            return handleResponse(response);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(String.format("发生异常，请检查连接、参数是否正确[%s]！ \n %s", url, e.getMessage()));
        } finally {
            if (httpMethod instanceof HttpPost) {
                try {
                    EntityUtils.consume(((HttpPost) httpMethod).getEntity());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private HttpRequestBase createHttpMethod(Object payload) {
        HttpRequestBase httpMethod;
        if ("GET".equalsIgnoreCase(action)) {
            httpMethod = new HttpGet();
            setURI((HttpGet) httpMethod, payload);
        } else {
            httpMethod = new HttpPost(url);
            setEntity((HttpPost) httpMethod, payload);
        }
        RequestConfig requestConfig = SpringManager.getBean(RequestConfig.class);
        httpMethod.setConfig(requestConfig);
        if (headers != null && headers.size() > 0) {
            headers.forEach((k, v) -> httpMethod.setHeader(k, Tools.replace(v)));
        }
        return httpMethod;
    }

    private void setURI(HttpGet httpMethod, Object payload) {
        try {
            URIBuilder uriBuilder = new URIBuilder(url).setCharset(CHARSET_UTF8);
            if (payload instanceof Map) {
                ((Map) payload).forEach((k, v) -> uriBuilder.setParameter(String.valueOf(k), String.valueOf(v)));
            } else if (payload instanceof String) {
                uriBuilder.setCustomQuery((String) payload);
            } else {
                throw new RuntimeException("Http请求参数类型错误 - " + payload.getClass());
            }
            httpMethod.setURI(uriBuilder.build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void setEntity(HttpPost httpMethod, Object payload) {
        HttpEntity httpEntity;
        if (payload instanceof Map) {
            /** content-Type: application/x-www-form-urlencoded **/
            List<NameValuePair> nvps = new ArrayList<>();
            ((Map) payload).forEach((k, v) -> nvps.add(new BasicNameValuePair(String.valueOf(k), String.valueOf(v))));
            httpEntity = new UrlEncodedFormEntity(nvps, CHARSET_UTF8);
            ((UrlEncodedFormEntity) httpEntity).setContentType(APPLICATION_FORM_URLENCODED_UTF8.toString());
        } else if (payload instanceof String) {
            /** content-Type: application/json **/
            httpEntity = new StringEntity((String) payload, "UTF-8");
            ((StringEntity) httpEntity).setContentType(ContentType.APPLICATION_JSON.toString());
        } else if (payload instanceof FormBodyPart[]) {
            /** content-type: multipart/form-data **/
            MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
            entityBuilder.setCharset(CHARSET_UTF8);
            entityBuilder.setContentType(MULTIPART_FORM_DATA_UTF8);
            Arrays.stream(((FormBodyPart[]) payload)).forEach(part -> entityBuilder.addPart(part));
            httpEntity = entityBuilder.build();
        } else {
            throw new RuntimeException("Http请求参数类型错误 - " + payload.getClass());
        }
        httpMethod.setEntity(httpEntity);
    }

    private R handleResponse(final CloseableHttpResponse response) throws IOException {
        final StatusLine statusLine = response.getStatusLine();
        final HttpEntity entity = response.getEntity();
        String result = EntityUtils.toString(entity, CHARSET_UTF8);
        if (abnormal(statusLine.getStatusCode(), result)) {
            EntityUtils.consume(entity);
            throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
        }
        if (returnHeaders) {
            Map<String, String> headers = new HashMap<>();
            for (Header header : response.getAllHeaders()) {
                headers.put(header.getName(), header.getValue());
            }
            Object o;
            try {
                o = JSONObject.parseObject(result);
            } catch (Exception e) {
                o = result;
            }
            return (R) MapUtil.mapper("headers", headers, "body", o);
        }
        return (R) result;
    }

    private boolean abnormal(int statusCode, String result) {
        /** 针对于APP返回状态码做特殊处理 **/
        if ("APP".equals(this.responseType)) {
            try {
                JSONObject.parseObject(result);
                return false;
            } catch (Exception e) {
                return true;
            }
        }
        return statusCode != HttpStatus.SC_OK;
    }
}
